home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 090 / pctj8404.arc / INTERF.ASM < prev    next >
Assembly Source File  |  1986-09-14  |  20KB  |  453 lines

  1.  
  2. ;                Keyin/Display/Clock Interface Routines.
  3. ;
  4. ;         These routines provide a standard interface to the keyboard and
  5. ;         display of the IBM personal computer.  
  6. ;
  7. ;         Copyright 1983 by John Cole, Duncanville, Texas. 
  8. ;
  9. ;
  10. ;         This routine is the interface to the display of the machine.  It
  11. ;         interprets a string of characters terminated by either a C$EOS 
  12. ;         character or a carriage return, sending the non-control characters 
  13. ;         to the display, and interpreting the controls to perform various
  14. ;         functions.
  15. ;
  16. ;         ON ENTRY:
  17. ;             SI -- ADDRESS OF STRING TO BE DISPLAYED.
  18. ;
  19. ;         ON EXIT:
  20. ;             SI -- POINTS PAST LAST CHARACTER DISPLAYED.
  21. ;             DL -- INDETERMINATE.
  22. ;             AL -- INDETERMINATE.
  23. ;
  24. ;         Equates for the various control characters 
  25. ;
  26. C$NUL     EQU    00H               ; NULL CHARACTER
  27. C$EOS     EQU    03H               ; DISPLAY STRING TERMINATOR
  28. C$BELL    EQU    07H               ; BELL CHARACTER
  29. C$CPOS    EQU    09H               ; CURSOR POSITION (H,V) FOLLOWS
  30. C$CR      EQU    0DH               ; END WITH CARRIAGE RETURN, LINE FEED
  31. C$LF      EQU    0AH               ; LINE FEED
  32. C$BS      EQU    08H               ; BACKSPACE
  33. C$EEOF    EQU    011H              ; ERASE TO END OF FRAME
  34. C$EEOL    EQU    012H              ; ERASE TO END OF LINE
  35. C$RU      EQU    013H              ; SCROLL THE SCREEN UP
  36. C$RD      EQU    014H              ; SCROLL THE SCREEN DOWN
  37. C$ESC     EQU    01BH              ; ESCAPE/CANCEL
  38. ;
  39. ;Macro Definitions
  40. ;
  41. SVC       MACRO  FUNC
  42.           MOV    AH,FUNC
  43.           INT    21H
  44.           ENDM
  45. ;
  46. BIOS      MACRO  FUNC
  47.           INT    10H
  48.           ENDM
  49. ;
  50. ;
  51. ;
  52. S$DSPLY   PROC    NEAR               ; DISPLAY PROCEDURE
  53.           PUSH    BX                 ; SAVE REGISTERS
  54. ;
  55. S$DSPLY0:
  56.           MOV     DL,[SI]            ; GET THE CHARACTER
  57.           INC     SI                 ; MOVE THE POINTER
  58.           CMP     DL,C$EOS           ; SEE IF END OF STRING
  59.           JNE     S$DSPLY1           ; BRANCH IF NOT
  60.           POP     BX                 ; RESTORE REGISTERS
  61.           RET                        ; ELSE EXIT
  62. ;
  63. ;         HERE CHECK FOR THE VARIOUS CONTROL CHARACTERS, AND TAKE APPROPRIATE
  64. ;         ACTION IF WE GET THEM.  THE FIRST CODE IS SCROLL UP.
  65. ;
  66. S$DSPLY1: CMP    DL,C$RU             ; SEE IF ROLL UP
  67.           JNE    S$DSPLY1A           ; IF NOT, TRY SOMETHING ELSE
  68. ;
  69.           MOV    CX,0                ; SET TOP OF SCREEN
  70.           MOV    DX,2479H            ; SET BOTTOM OF SCREEN WINDOW
  71.           MOV    BH,7                ; ATTRIBUTE OF BLANK LINE
  72.           MOV    AX,601H             ; SET SCROLL CODE FOR BIOS, 1 LINE
  73.           BIOS
  74.           JMP    S$DSPLY0            ; LOOP BACK FOR NEXT CHARACTER
  75. ;
  76. ;         CHECK FOR SCROLL DOWN.
  77. ;
  78. S$DSPLY1A:
  79.           CMP    DL,C$RD             ; CHECK FOR ROLL-DOWN CODE
  80.           JNE    S$DSPLY2            ; JUMP IF NOT
  81.           MOV    CX,0                ; SET TOP POSITION
  82.           MOV    DX,2479H            ; AND BOTTOM OF SCREEN
  83.           MOV    BH,7                ; ATTRIBUTES OF BLANK LINE
  84.           MOV    AX,701H             ; SCROLL 1 LINE, BIOS CODE IN AH
  85.           BIOS                       ; DO IT
  86.           JMP    S$DSPLY0            ; BACK FOR NEXT CHARACTER
  87. ;
  88. ;         CHECK FOR CURSOR POSITONING HERE.
  89. ;
  90. S$DSPLY2:
  91.           CMP    DL,C$CPOS         ; CHECK IT
  92.           JNE    S$DSPLY3          ; JUMP IF NOT EQUAL
  93.           MOV    AH,2              ; FUNCTION NUMBER
  94.           MOV    DX,[SI]           ; GET POSITION
  95.           ADD    SI,2              ; MOVE PAST THE POSITION
  96.           MOV    BH,0              ; SET DISPLAY PAGE
  97.           BIOS                     ; CALL BIOS FOR DISPLAY CONTROL
  98.           JMP    S$DSPLY0          ; LOOP BACK FOR NEXT CHARACTER
  99. ;
  100. ;         CHECK FOR CARRIAGE RETURN, WHICH DOES A LINE FEED AS WELL. THIS IS
  101. ;         ALSO TREATED AS A STRING TERMINATOR.
  102. ;
  103. S$DSPLY3:
  104.           CMP    DL,C$CR           ; CHECK FOR CARRIAGE RETURN
  105.           JNE    S$DSPLY4          ; JUMP IF NOT
  106.           SVC    DSPCHAR$          ; ELSE PUT OUT THE CHARACTER
  107.           POP    BX                ; RESTORE REGISTERS
  108.           RET                      ; AND EXIT
  109. ;
  110. ;         ERASE SCREEN FROM CURRENT CURSOR POSITION TO END OF FRAME.  THE 
  111. ;         LOGIC IS TO READ THE CURRENT CURSOR POSITION, THEN WRITE BLANKS TO
  112. ;         THE SCREEN FROM THERE TO THE END OF THE SCREEN.  FINALLY, WE RESET
  113. ;         THE CURSOR POSITION TO ITS FORMER VALUE.
  114. ;
  115. S$DSPLY4:
  116.           CMP    DL,C$EEOF         ; CHECK FOR ERASE TO END OF FRAME CODE
  117.           JNE    S$DSPLY5          ; IF NOT, TRY NEXT ONE
  118. ;
  119.           MOV    AH,3              ; CODE FOR READING THE CURSOR POSITION
  120.           BIOS                     ; GET IT
  121.           PUSH   DX                ; AND STACK IT
  122.           CALL   S$SCLR            ; CLEAR THE REMAINDER OF THIS LINE
  123.           POP    DX                ; RESTORE ORIGINAL CURSOR POSITION
  124.           MOV    AH,2              ; SET ORIGINAL CURSOR POSITION
  125.           BIOS
  126.           JMP    S$DSPLY0          ; AND LOOP BACK
  127. ;
  128. ;         CHECK FOR ERASE TO END OF LINE CODE.  IF SO, JUST CALL THE ROUTINE
  129. ;         TO CLEAR THE END OF THE LINE, THEN RESTORE THE CURRENT CURSOR.
  130. ;
  131. S$DSPLY5:
  132.           CMP    DL,C$EEOL          ; CHECK FOR THE CODE
  133.           JNE    S$DSPLY8           ; IF NOT, TRY SOMETHING ELSE
  134. ;
  135.           MOV    AH,3               ; READ CURSOR POSITION
  136.           BIOS                      ; GET IT
  137.           PUSH   DX                 ; AND STACK IT
  138.           CALL   S$DCLR             ; CLEAR THE REST OF THIS LINE
  139.           POP    DX                 ; RESTORE OLD CURSOR POSITION
  140.           MOV    AH,2               ;
  141.           BIOS                      ;
  142.           JMP    S$DSPLY0           ; AND BACK TO THE MAIN LOOP
  143. ;
  144. ;         CHECK FOR BELL CHARACTER, CAUSES BEEP.
  145. ;
  146. S$DSPLY8:
  147.           CMP    DL,C$BELL          ; TRY BELL CHARACTER
  148.           JNE    S$DSPLY9           ; JUMP IF NOT
  149. ;
  150.           CALL   S$BEEP             ; BEEP THE SPEAKER
  151.           JMP    S$DSPLY0           ; AND RETURN TO THE LOOP
  152. ;
  153. ;         HERE THE CHARACTER IS NOT ONE OF THE CONTROLS, SO JUST OUTPUT IT
  154. ;         TO THE SCREEN, THEN LOOP.
  155. ;
  156. S$DSPLY9: SVC    DSPCHAR$          ; DISPLAY THE CHARACTER
  157.           JMP    S$DSPLY0          ; AND LOOP BACK
  158. ;
  159. ;
  160. ;         SUBROUTINE TO CLEAR A SINGLE ROW FROM THE POSITION IN DL TO THE
  161. ;         END OF THE LINE.  ON EXIT, DL HAS ZERO. 
  162. ;
  163. S$DCLR:
  164.           PUSH   DX                ; SAVE POSITION OVER LOOP
  165.           MOV    DH,80             ; SET MAXIMUM VALUE
  166.           SUB    DH,DL             ; COMPUTE NUMBER OF COLUMNS TO CLEAR
  167.           MOV    DL,' '            ; FILL CHARACTER
  168. ;
  169. S$DCLR1:
  170.           SVC    DSPCHAR$          ; WRITE THE CHARACTER
  171.           DEC    DH                ; DECREMENT THE COUNTER
  172.           JNZ    S$DCLR1           ; LOOP BACK IF NOT DONE
  173.           POP    DX                ; RESTORE THE POSITION
  174.           MOV    DL,0              ; CLEAR COLUMN NUMBER
  175.           RET                      ; AND EXIT
  176. S$DSPLY   ENDP
  177. ;
  178. ;         SUBROUTINE TO CLEAR THE SCREEN FROM THE CURSOR POSITION TO THE END OF
  179. ;         THE SCREEN. 
  180. ;
  181. S$SCLR:   PUSH   CX                ; SAVE SOME REGISTERS
  182.           PUSH   ES                ;
  183.           PUSH   DI                ;
  184.           MOV    AL,DH             ; GET CURSOR ROW
  185.           PUSH   DX                ; STACK CURSOR POSITION
  186.           MOV    DX,160            ; NUMBER OF BYTES IN A ROW
  187.           MOV    AH,0              ; CLEAR HIGH BYTE
  188.           MUL    DL                ; COMPUTE OFFSET FOR ROW
  189.           POP    DX                ; RESTORE POSITION
  190.           MOV    DH,0              ; CLEAR HIGH BYTE OF COLUMN
  191.           SAL    DX,1              ; COMPUTE IN WORDS
  192.           ADD    AX,DX             ; COMPUTE OFFSET
  193.           MOV    DI,AX             ; COPY TO PROPER REGISTER
  194.           PUSH   DI                ; STACK BUFFER OFFSET
  195.           INT    11H               ; GET EQUIPMENT TYPE
  196.           MOV    DI,AX             ; COPY IT
  197.           MOV    AX,0B800H         ; SEGMENT ADDRESS FOR COLOR CARD
  198.           AND    DI,30H            ; SEE IF COLOR OR BLACK AND WHITE CARD
  199.           CMP    DI,30H            ; 
  200.           JNE    S$SCLR1           ; JUMP IF COLOR 
  201.           MOV    AX,0B000H         ; SET UP BLACK AND WHITE
  202. S$SCLR1:  POP    DI                ; RESTORE BUFFER OFFSET
  203.           MOV    ES,AX             ; SET UP CARD BASE AS EXTRA SEGMENT
  204.           MOV    CX,80*25*2        ; FILL LENGTH IN WORDS
  205.           SUB    CX,DI             ; SUBTRACT OFF LENGTH BEFORE POSITION
  206.           MOV    AX,' '+7*256      ; FILL CHARACTER AND ATTRIBUTE
  207.           REP   STOSW              ; STORE IT ALL
  208.           POP    DI                ; RESTORE REGISTERS
  209.           POP    ES                ;
  210.           POP    CX                ;
  211.           RET                      ; BACK TO CALLER
  212. ;
  213.           PAGE
  214. ;
  215. ;                 CHARACTER DISPLAY SUBROUTINE.
  216. ;
  217. ;         THIS ROUTINE PUTS THE CHARACTER ON THE SCREEN.  HOWEVER, IF IT WOULD
  218. ;         GO PAST THE END OF THE LINE, NO WRAPAROUND TAKES PLACE.
  219. ;
  220. S$DSPCHR  PROC    NEAR               ; ENTRY POINT FOR CHARACTER DISPLAY
  221.           PUSH    BX                 ; STACK ATTRIBUTE
  222.           PUSH    CX                 ; AND COUNTER REGISTER
  223.           PUSH    DX                 ;
  224.           MOV     CX,1               ; ONLY ONE CHARACTER GOING OUT
  225.           MOV     BH,0               ; DISPLAY PAGE IN USE
  226.           MOV     AH,9               ; DISPLAY CHARACTER/ATTRIBUTE
  227.           INT     10H                ; CALL THE ROM
  228.           MOV     AH,3               ; GET CURRENT CURSOR POSITION
  229.           INT     10H                ;
  230.           CMP     DL,79              ; CHECK FOR PAST END OF LINE
  231.           JE      S$DSPCHR1          ;
  232.           INC     DL                 ; IF NOT, MOVE TO NEXT COLUMN
  233.           MOV     AH,2               ; NOW SET THE CURSOR
  234.           INT     10H                ;
  235. S$DSPCHR1: POP    DX                 ;
  236.           POP     CX                 ; RESTORE REGISTERS
  237.           POP     BX                 ; . . .
  238.           RET                        ; AND BACK TO CALLER
  239. S$DSPCHR  ENDP                       ; END OF CHARACTER DISPLAY PROCEDURE
  240. ;
  241. ;
  242. ;                 RETURN CURRENT CURSOR POSITION.
  243. ;
  244. S$CURPOS  PROC    NEAR               ; GET CURSOR POSITION IN DX
  245.           PUSH    CX                 ; STACK OVER CALL
  246.           PUSH    BX                 ;
  247.           MOV     BH,0               ; SET THE DISPLAY PAGE
  248.           MOV     AH,3               ; VIDEO I/O PARAMETER
  249.           INT     10H                ; CALL THE ROM
  250.           POP     BX                 ; RESTORE CALLER'S BX
  251.           POP     CX                 ; RESTORE CALLER'S CX
  252.           RET                        ; EXIT WITH POSITION
  253. S$CURPOS  ENDP                       ; END OF PROCEDURE
  254. ;
  255. ;
  256.           PAGE
  257. ;
  258. ;         Routine to key in a line of a specified length into a buffer.
  259. ;
  260. ;         On entry:
  261. ;             SI -- Address of buffer.
  262. ;             CX -- Number of characters to accept.
  263. ;
  264. ;         On Exit:
  265. ;             SI -- Unchanged.
  266. ;
  267. S$KEYIN   PROC   NEAR              ; PROCEDURE ENTRY POINT
  268.           PUSH   SI                ; STACK BUFFER ADDRESS
  269. ;
  270. S$KEYIN0: SVC    KEYINX$           ; GET A CHARACTER
  271. ;
  272. ;         CHECK FOR BACKSPACE AND CANCEL HERE.
  273. ;
  274.           CMP    AL,C$ESC          ; CHECK FOR ESCAPE
  275.           JE     S$KEYINA          ; JUMP IF SO
  276.           CMP    AL,C$BS           ; TEST WHAT WE GOT
  277.           JNE    S$KEYIN3          ; IF NOT, WAS SOMETHING ELSE
  278. ;
  279. S$KEYINA: MOV    BP,SP             ; GET STACK POINTER
  280.           MOV    DH,AL             ; COPY CHARACTER THAT GOT US HERE
  281. S$KEYINB: CMP    SI,[BP]           ; CHECK FOR BEGINNING OF LINE
  282.           JE     S$KEYIN0          ; LOOP BACK IF SO
  283.           MOV    DL,C$BS           ; GET A BACKSPACE
  284.           SVC    DSPCHAR$          ; BACK OVER CHARACTER
  285.           MOV    DL,' '            ; GET BLANK FOR CURRENT POSITION
  286.           SVC    DSPCHAR$          ; PUT IT OUT
  287.           MOV    DL,C$BS           ; NOW BACKSPACE AGAIN
  288.           SVC    DSPCHAR$          ; SINCE WE HAVE PUT OUT THE BLANK
  289.           INC    CX                ; AND INCREMENT THE COUNTER
  290.           DEC    SI                ; BACK UP STORAGE POINTER
  291.           CMP    DH,C$ESC          ; CHECK FOR ESCAPE/CANCEL
  292.           JE     S$KEYINB          ; LOOP BACK IF SO
  293.           JMP    S$KEYIN0          ; AND LOOP BACK FOR THE NEXT ONE
  294. ;
  295. S$KEYIN3:
  296.           MOV    [SI],AL           ; STORE IT
  297.           INC    SI                ; MOVE THE POINTER
  298.           CMP    AL,C$CR           ; CHECK FOR CARRIAGE RETURN
  299.           JNE    S$KEYIN4          ; JUMP IF NOT
  300.           POP    SI                ; RESTORE STRING ADDRESS
  301.           RET                      ; ELSE EXIT IF END OF LINE
  302. ;
  303. S$KEYIN4: MOV    DL,AL             ; COPY THE CHARACTER
  304.           SVC    DSPCHAR$          ; PUT IT ON THE SCREEN
  305.           LOOPNZ S$KEYIN0          ; LOOP BACK FOR NEXT CHARACTER
  306. ;
  307. ;         HERE THE COUNTER HAS DECREMENTED TO ZERO, SO WE SIT IN A HARD LOOP
  308. ;         WAITING FOR A CARRIAGE RETURN.
  309. ;
  310. S$KEYIN5: SVC    KEYINX$           ; GET A CHARACTER
  311.           CMP    AL,C$CR           ; CHECK FOR END
  312.           JE     S$KEYIN6          ; EXIT IF IT IS
  313.           CMP    AL,C$BS           ; CHECK FOR BACKSPACE
  314.           JE     S$KEYINA          ; TAKE CARE OF THAT IF SO
  315.           CMP    AL,C$ESC          ; CHECK FOR ESCAPE, TREATED AS CANCEL
  316.           JE     S$KEYINA          ; AND JUMP IF SO
  317.           CALL   S$BEEP            ; BEEP
  318.           MOV    DL,C$BS           ; GET BACKSPACE
  319.           SVC    DSPCHAR$          ; AND BACK OVER BAD CHARACTER
  320.           JMP    S$KEYIN5          ; AND LOOP BACK
  321. ;
  322. S$KEYIN6: MOV    [SI],AL           ; STORE THE TERMINATOR
  323.           POP    SI                ; RESTORE STRING ADDRESS
  324.           RET                      ; AND EXIT
  325. S$KEYIN   ENDP
  326. ;
  327.           PAGE
  328. ;         BEEP ROUTINE
  329. ;
  330. ;         THIS ROUTINE CAUSES A BEEP AT THE SPEAKER OF THE PC.  
  331. ;
  332. TIMER$    EQU     40H                ; TIMER PORT
  333. PORT$B    EQU     61H                ; 8255 PORT B ADDRESS
  334. ;
  335. S$BEEP    PROC    NEAR               ; START OF PROCEDURE
  336.           MOV     AL,10110110B       ; GET TIMER SETTING
  337.           OUT     TIMER$+3,AL        ; SET TIMER MODE
  338.           MOV     AX,533H            ; DIVISOR FOR 1000 HZ
  339.           OUT     TIMER$+2,AL        ; SET COUNT LSB
  340.           MOV     AL,AH              ; 
  341.           OUT     TIMER$+2,AL        ; SET COUNT MSB
  342.           IN      AL,PORT$B          ; GET CURRENT PORT MODE
  343.           MOV     AH,AL              ; SAVE IT
  344.           OR      AL,3               ; TURN SPEAKER ON
  345.           OUT     PORT$B,AL
  346.           MOV     CX,32762           ; COUNTER FOR ABOUT 250 MS WAIT
  347. S$BEEP1:  LOOP    S$BEEP1            ; DELAY BEFORE TURNING OFF
  348.           MOV     AL,AH              ; RECOVER OLD PORT VALUE
  349.           OUT     PORT$B,AL          ; SET IT THE WAY IT WAS
  350.           RET                        ; AND EXIT TO CALLER
  351. S$BEEP    ENDP                       ; END OF ROUTINE
  352. ;
  353.           PAGE
  354. ;
  355. ;                 GET TIME AS A STRING.
  356. ;
  357. ;         This routine returns a string containing the current time as read
  358. ;         from the system clock.  The string is of the form HH:MM:SS.
  359. ;
  360. ;         On entry:
  361. ;             SI -- Address into which string is to be placed.
  362. ;
  363. ;         On Exit:
  364. ;             AX -- Changed.
  365. ;
  366. S$TIME    PROC    NEAR               ; START OF TIME PROCEDURE
  367.           PUSH    SI                 ; STACK INCOMING REGISTERS
  368.           PUSH    DX                 ; . . .
  369.           PUSH    CX                 ; . . .
  370.           SVC     TIME$              ; GET THE TIME
  371.           PUSH    DX                 ; STACK SECONDS
  372.           MOV     AL,CH              ; THE HOURS ARE GOOD
  373.           CBW                        ; CONVERT TO WORD
  374.           MOV     DL,10              ; SEPARATE THE DIGITS
  375.           DIV     DL                 ;
  376.           ADD     AX,'00'            ; CONVERT TO ASCII
  377.           MOV     [SI],AX            ; SAVE HOURS
  378.           MOV     BYTE PTR 2[SI],':' ; STORE SEPARATOR
  379.           ADD     SI,3               ; MOVE STORE POINTER
  380.           MOV     AL,CL              ; NOW GET THE MINUTES
  381.           CBW                        ; CONVERT
  382.           MOV     DL,10              ; GET DIVISOR
  383.           DIV     DL                 ; SPLIT THE DIGITS
  384.           ADD     AX,'00'            ; CONVERT TO ASCII
  385.           MOV     [SI],AX            ; STORE IT, LSB,MSB
  386.           MOV     BYTE PTR 2[SI],':' ; AND KEEP THEM APART
  387.           ADD     SI,3               ; MOVE MICKEY'S BIG HAND
  388.           POP     DX                 ; RESTORE SECONDS
  389.           MOV     AL,DH              ; SECONDS, PLEASE
  390.           CBW                        ; CLEAR HIGH BYTE OF DIVIDEND
  391.           MOV     DL,10              ; DIVISOR
  392.           DIV     DL                 ; SPLIT
  393.           ADD     AX,'00'            ; CONVERT TO ASCII
  394.           MOV     [SI],AX            ; STORE
  395.           POP     CX                 ; RESTORE CALLER'S REGISTERS
  396.           POP     DX                 ; . . .
  397.           POP     SI                 ; . . .
  398.           RET                        ; AND GO HOME
  399. S$TIME    ENDP                       ; END OF TIME CONVERSION
  400. ;
  401.           PAGE
  402. ;
  403. ;                 DATE CONVERSION ROUTINE.
  404. ;
  405. ;         This routine gets the system date and converts it into a string of
  406. ;         the form MM/DD/YY.  
  407. ;
  408. ;         On Entry:
  409. ;             SI -- Address into which date is to be returned.
  410. ;
  411. ;         On Exit:
  412. ;             AX -- Changed.
  413. ;             All other registers preserved.
  414. ;
  415. S$DATE    PROC    NEAR               ; DATE CONVERSION PROCEDURE
  416.           PUSH    SI                 ; STACK CALLER'S REGISTERS
  417.           PUSH    CX                 ; . . .
  418.           PUSH    DX                 ; . . .
  419.           SVC     DATE$              ; CALL UP THE DATE
  420.           PUSH    DX                 ; SAVE THE DAY
  421.           MOV     AL,DH              ; GET THE MONTH
  422.           CBW                        ; EXPAND IT OUT
  423.           MOV     DL,10              ; GET DIVISOR
  424.           DIV     DL                 ; CONVERT MONTH
  425.           ADD     AX,'00'            ; CONVERT TO ASCII
  426.           MOV     [SI],AX            ; STORE IT
  427.           MOV     BYTE PTR 2[SI],'/' ; STORE SEPARATOR
  428.           ADD     SI,3               ; MOVE TO DAY
  429.           POP     DX                 ; RESTORE FROM STACK
  430.           MOV     AL,DL              ; WHAT DAY IS IT?
  431.           CBW                        ; EXPAND IT
  432.           MOV     DL,10              ; DIVISOR
  433.           DIV     DL                 ; SPLIT THE DIGITS APART
  434.           ADD     AX,'00'            ; CONVERT TO ASCII
  435.           MOV     [SI],AX            ; STORE IT
  436.           MOV     BYTE PTR 2[SI],'/' ; STORE SEPARATOR
  437.           ADD     SI,3               ; MOVE TO YEAR
  438.           SUB     CX,1900            ; SUBTRACT BIAS FROM YEAR
  439.           CMP     CX,100             ; SEE IF TURN OF MILLENIUM PAST
  440.           JL      S$DATE1            ; JUMP IF NOT (HIGH PROBABILITY)
  441.           SUB     CX,100             ; ELSE MAKE SURE WE GET JUST 2 DIGITS
  442. S$DATE1:  MOV     AL,CL              ; GET THE YEAR
  443.           CBW                        ; CLEAR HIGH BYTE
  444.           MOV     DL,10              ; DIVISOR
  445.           DIV     DL                 ; SPLIT
  446.           ADD     AX,'00'            ; CONVERT
  447.           MOV     [SI],AX            ; STORE
  448.           POP     DX                 ; RESTORE CALLER'S REGISTERS
  449.           POP     CX                 ; . . .
  450.           POP     SI                 ; . . .
  451.           RET                        ; EXIT TO CALLER 
  452. S$DATE    ENDP                       ; THE DATE IS OVER
  453.